package com.skzh.flightplans.utils;

import com.skzh.flightplans.domain.FlightPlans;
import com.skzh.flightplans.domain.Waypoints;
import com.skzh.common.core.domain.AjaxResult;
import com.skzh.common.utils.SecurityUtils;
import com.skzh.common.config.SkzhConfig;
import com.skzh.common.core.domain.model.LoginUser;
import com.skzh.flightplans.service.IFlightPlansService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

@Component
//@RequiredArgsConstructor
public class FlightPlansUtil {
    //字段注入
//    @Autowired
//    private IFlightPlansService flightPlansService;
//
//    @Autowired
//    private TaskStatusManager taskStatusManager;
    private final IFlightPlansService flightPlansService;
    private final TaskStatusManager taskStatusManager;

    //构造器注入(推荐)，将依赖的对象实例，注入到参数里，然后赋值给类的成员变量
    @Autowired
    public FlightPlansUtil(IFlightPlansService flightPlansService,
                           TaskStatusManager taskStatusManager) {
        this.flightPlansService = flightPlansService;
        this.taskStatusManager = taskStatusManager;
    }

    //lombok自动注入
//    private final IFlightPlansService flightPlansService;
//    private final TaskStatusManager taskStatusManager;

    /**
     * 处理航线上传的主方法
     */
    public AjaxResult processFlightPlansUpload(FlightPlans flightPlans) {
        try {
            // 1. 基本验证和信息准备（在主线程中执行）
            if (flightPlans == null || flightPlans.getSourceFilePath() == null || flightPlans.getSourceFileName() == null) {
                return AjaxResult.error("上传信息不完整");
            }

            String taskId = UUID.randomUUID().toString();
            TaskStatus initialStatus = TaskStatus.createProcessing(taskId);
            taskStatusManager.updateTaskStatus(taskId, initialStatus);

            // 2. 创建新的航线对象并设置基本信息（在主线程中执行）
            FlightPlans newFlightPlans = new FlightPlans();
            LoginUser loginUser = SecurityUtils.getLoginUser();
            newFlightPlans.setCreateBy(loginUser.getUsername());
            newFlightPlans.setHiDeptName(flightPlans.getHiDeptName());
            newFlightPlans.setSourceFilePath(flightPlans.getSourceFilePath());
            newFlightPlans.setSourceFileName(flightPlans.getSourceFileName());


            /**
             * 异步处理文件
             * 新加了一个类用于管理文件处理的状态，前端轮询调用，查看这个处理状态并返回提示。
             *
             */
            CompletableFuture.runAsync(() -> {
                try {
                    processFileAsync(newFlightPlans);
                    taskStatusManager.updateTaskStatus(taskId, TaskStatus.createSuccess(taskId, "文件处理成功"));
                } catch (Exception e) {
                    taskStatusManager.updateTaskStatus(taskId, TaskStatus.createFailed(taskId, e.getMessage()));
                }
            });

            return AjaxResult.success("文件上传成功，正在后台处理中", taskId);


//            processFileAsync(newFlightPlans);
//            return AjaxResult.success("文件上传成功，正在后台处理中");

            // 3. 异步处理文件并等待结果
//            CompletableFuture<Void> future = processFileAsync(newFlightPlans);
//
//            try {
//                // 等待异步任务完成，设置超时时间为30秒
//                future.get();
//                return AjaxResult.success("文件处理成功");
//            } catch (Exception e) {
//                // 获取异步任务中的实际异常
//                Throwable cause = e.getCause();
//                String errorMessage = cause != null ? cause.getMessage() : e.getMessage();
//                return AjaxResult.error("文件处理失败: " + errorMessage);
//            }
        } catch (Exception e) {
            return AjaxResult.error("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 异步处理文件
     * @Async 注解是 Spring 提供的一个注解，用于标记一个方法为异步方法。当一个方法被标记为 @Async 时，Spring 会通过一个线程池SimpleAsyncTaskExecutor来异步执行该方法。
     * @Async("threadPoolTaskExecutor") 注解指定自定义线程池的 bean 名称为 "threadPoolTaskExecutor"。这个线程池可以在 Spring 的配置文件中定义。
     */
    //无返回值时
    //无返回值时，确实可以只使用@Async注解，但Spring 的 @Async 注解有一个重要的限制：它只能通过代理对象调用才能生效。当在同一个类中直接调用 @Async 方法时，会绕过 Spring 的代理机制，导致异步失效。
    //所以单独使用@Async注解时，要不就把方法移到其他类里面。要不就使用 ApplicationContext 获取代理对象：
//    @Async("threadPoolTaskExecutor")
//    public void processFileAsync(FlightPlans newFlightPlans) {
//        try {
//            // 1. 准备文件路径
//            String fileUrl = "http://localhost:8280/profile/upload";
//            String filePath = SkzhConfig.getUploadPath();
//            String localFullPath = newFlightPlans.getSourceFilePath().replace(fileUrl, filePath);
//            String destDir = filePath + "/extracted/";
//
//            // 2. 创建解压目录
//            File dir = new File(destDir);
//            if (!dir.exists()) {
//                dir.mkdirs();
//            }
//
//            // 3. 处理文件
//            List<Waypoints> waypointsList = new ArrayList<>();
//            File uploadedFile = new File(localFullPath);
//            File renamedFile = new File(localFullPath.replace(".kmz", ".zip"));
//
//            if (uploadedFile.renameTo(renamedFile)) {
//                try (FileInputStream fileInputStream = new FileInputStream(renamedFile);
//                     ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
//
//                    processZipFile(zipInputStream, destDir, newFlightPlans, waypointsList);
//                }
//            } else {
//                throw new IOException("Failed to rename file from .kmz to .zip");
//            }
//
//            // 4. 保存到数据库
//            flightPlansService.insertFlightPlansAndWaypoints(newFlightPlans, waypointsList);
//            //return CompletableFuture.completedFuture( AjaxResult.success("文件处理成功"));
//            System.out.println("文件处理成功");
//        } catch (Exception e) {
//            //return CompletableFuture.completedFuture( AjaxResult.error(e.getMessage()));
//            System.out.println("文件处理失败");
//        }
//    }

    //有返回值时，@Async("threadPoolTaskExecutor")和 CompletableFuture同时写，会各创建一个异步任务且各创建一个线程池，增加代码复杂性增加资源浪费
    //同时写的话，使用的不是@Async指定的线程池，而是CompletableFuture的默认线程池，这个默认线程池是可以复用共享的
    //@Async("threadPoolTaskExecutor")
//    public CompletableFuture<AjaxResult> processFileAsync(FlightPlans newFlightPlans) {
//        return CompletableFuture.supplyAsync(() -> {
//            try {
//                // 1. 准备文件路径
//                String fileUrl = "http://localhost:8280/profile/upload";
//                String filePath = SkzhConfig.getUploadPath();
//                String localFullPath = newFlightPlans.getSourceFilePath().replace(fileUrl, filePath);
//                String destDir = filePath + "/extracted/";
//
//                // 2. 创建解压目录
//                File dir = new File(destDir);
//                if (!dir.exists()) {
//                    dir.mkdirs();
//                }
//
//                // 3. 处理文件
//                List<Waypoints> waypointsList = new ArrayList<>();
//                File uploadedFile = new File(localFullPath);
//                File renamedFile = new File(localFullPath.replace(".kmz", ".zip"));
//
//                if (uploadedFile.renameTo(renamedFile)) {
//                    try (FileInputStream fileInputStream = new FileInputStream(renamedFile);
//                         ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
//
//                        processZipFile(zipInputStream, destDir, newFlightPlans, waypointsList);
//                    }
//                } else {
//                    throw new IOException("Failed to rename file from .kmz to .zip");
//                }
//
//                // 4. 保存到数据库
//                flightPlansService.insertFlightPlansAndWaypoints(newFlightPlans, waypointsList);
//                System.out.println("文件处理成功");
//                //有返回值时，使用CompletableFuture.supplyAsync()写法， 返回值类型CompletableFuture<AjaxResult>
//                return AjaxResult.success("文件处理成功");
//            } catch (Exception e) {
//                return AjaxResult.error(e.getMessage());
//            }
//        });
//    }


//    public CompletableFuture<Void> processFileAsync(FlightPlans newFlightPlans) {
//        return CompletableFuture.runAsync(() -> {
//            try {
//                // 1. 准备文件路径
//                String fileUrl = "http://localhost:8280/profile/upload";
//                String filePath = SkzhConfig.getUploadPath();
//                String localFullPath = newFlightPlans.getSourceFilePath().replace(fileUrl, filePath);
//                String destDir = filePath + "/extracted/";
//
//                // 2. 创建解压目录
//                File dir = new File(destDir);
//                if (!dir.exists()) {
//                    dir.mkdirs();
//                }
//
//                // 3. 处理文件
//                List<Waypoints> waypointsList = new ArrayList<>();
//                File uploadedFile = new File(localFullPath);
//                File renamedFile = new File(localFullPath.replace(".kmz", ".zip"));
//
//                if (uploadedFile.renameTo(renamedFile)) {
//                    try (FileInputStream fileInputStream = new FileInputStream(renamedFile);
//                         ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
//
//                        processZipFile(zipInputStream, destDir, newFlightPlans, waypointsList);
//                    }
//                } else {
//                    throw new IOException("Failed to rename file from .kmz to .zip");
//                }
//
//                // 4. 保存到数据库
//                flightPlansService.insertFlightPlansAndWaypoints(newFlightPlans, waypointsList);
//                System.out.println("文件处理成功");
//            } catch (Exception e) {
//                System.out.println("文件处理失败: " + e.getMessage());
//                // 将异常包装并重新抛出，这样前端就能收到错误信息
//                throw new RuntimeException("文件处理失败: " + e.getMessage(), e);
//            }
//        });
//    }


    private void processFileAsync(FlightPlans newFlightPlans) throws IOException {
        // 1. 准备文件路径
        String fileUrl = "http://localhost:8280/profile/upload";
        String filePath = SkzhConfig.getUploadPath();
        String localFullPath = newFlightPlans.getSourceFilePath().replace(fileUrl, filePath);
        String destDir = filePath + "/extracted/";

        // 2. 创建解压目录
        File dir = new File(destDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 3. 处理文件
        List<Waypoints> waypointsList = new ArrayList<>();
        File uploadedFile = new File(localFullPath);
        File renamedFile = new File(localFullPath.replace(".kmz", ".zip"));

        if (uploadedFile.renameTo(renamedFile)) {
            try (FileInputStream fileInputStream = new FileInputStream(renamedFile);
                 ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) {
                processZipFile(zipInputStream, destDir, newFlightPlans, waypointsList);
            }
        } else {
            throw new IOException("Failed to rename file from .kmz to .zip");
        }

        // 4. 保存到数据库
        flightPlansService.insertFlightPlansAndWaypoints(newFlightPlans, waypointsList);
    }

    /**
     * 处理ZIP文件
     */
    private void processZipFile(ZipInputStream zipInputStream, String destDir, FlightPlans newFlightPlans, List<Waypoints> waypointsList) throws IOException {
        ZipEntry entry;
        while ((entry = zipInputStream.getNextEntry()) != null) {
            File outFile = new File(destDir, entry.getName());
            if (entry.isDirectory()) {
                outFile.mkdirs();
            } else {
                new File(outFile.getParent()).mkdirs();
                try (FileOutputStream outputStream = new FileOutputStream(outFile)) {
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = zipInputStream.read(buffer)) > 0) {
                        outputStream.write(buffer, 0, len);
                    }
                }
            }
            if (entry.getName().endsWith(".kml")) {
                parseKmlFile(new File(destDir, entry.getName()), newFlightPlans, waypointsList);
            }
        }
    }

    /**
     * 解析KML文件
     */
    private void parseKmlFile(File kmlFile, FlightPlans newFlightPlans, List<Waypoints> waypointsList) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(kmlFile))) {
            String line;
            boolean inCoordinatesTag = false;

            while ((line = reader.readLine()) != null) {
                Pattern namePattern = Pattern.compile("<name>(.*?)</name>");
                Matcher nameMatcher = namePattern.matcher(line);
                if (nameMatcher.find()) {
                    String nameContent = nameMatcher.group(1);
                    newFlightPlans.setFpName(nameContent);
                }

                if (line.contains("<coordinates>")) {
                    inCoordinatesTag = true;
                    line = line.substring(line.indexOf("<coordinates>") + "<coordinates>".length()).trim();
                }

                if (inCoordinatesTag) {
                    if (line.contains("</coordinates>")) {
                        line = line.substring(0, line.indexOf("</coordinates>")).trim();
                        inCoordinatesTag = false;
                    }

                    String[] coordinatesArray = line.split("\\s+");
                    for (String coordinate : coordinatesArray) {
                        String[] parts = coordinate.split(",");
                        if (parts.length == 3) {
                            try {
                                double longitude = Double.parseDouble(parts[0]);
                                double latitude = Double.parseDouble(parts[1]);
                                double altitude = Double.parseDouble(parts[2]);

                                Waypoints waypoint = new Waypoints();
                                waypoint.setWpLongitude(longitude);
                                waypoint.setWpLatitude(latitude);
                                waypoint.setWpAltitude(altitude);
                                waypointsList.add(waypoint);
                            } catch (NumberFormatException e) {
                                System.err.println("Invalid coordinate format: " + coordinate);
                            }
                        }
                    }
                }
            }
        }
    }
}